/**
 * Class $$
 * @type Singleton
 * @author TCL
 * @vesion 1
 * @since 1
 */

$$ = {};
$$.version = 1.00;
$$.caches = {};

/**
 * Put the properties of some object to target object
 * param 0: the target object
 * param n: the source object
 * If only one param, target is a new object, source is the param
 * 
 * @param arguments
 * @return object the object after put
 */
$$.put = function(/*arguments*/){
	var srcs	= arguments
		,target	= srcs[0]
		,len	= srcs.length
		,i		= 1
		,src;
	
	// if only one param, target is a new object
	if(1 == len){
		target = {};
		i = 0;
	}
	
	for(; i < len; ++i){
		src = srcs[i];
		for(var j in src){
			target[j] = src[j];
		}
	}
	
	return target;
};

/**
 * Put the properties of some object to target object if don't already exist
 * 
 * @param arguments
 * @return object the object after put
 */
$$.putIf = function(/*arguments*/){
	var srcs	= arguments
		,target	= srcs[0]
		,p		= [].splice.call(srcs, 0, 1, {})
		,tmp	= $$.put.apply(this, p);
	
	for(var i in tmp){
		if('undefined' != target[i]) continue;
		target[i] = tmp[i];
	}
	
	return target;
};

/**
 * Put any method to $$ object such as getType, isArray,...
 */
$$.put($$, {
	/**
	 * get type of object
	 * 
	 * @param mixed o
	 * @return string
	 */
	getType: function(o){
		if('undefined' == o) return undefined;
		return Object.prototype.toString.call(o).match(/\[object (\w+)\]/)[1];
	}

	/**
	 * check if object is undefined
	 * @param mixed o
	 * @return boolean
	 */
	,isUndefined: function(o){
		return 'undefined' == typeof o;
	}
	
	/**
	 * check if object is undefined
	 * @param mixed o
	 * @return boolean
	 */
	,isDefined: function(o){
		return !$$.isUndefiend(o);
	}
	
	/**
	 * check if object is undefined
	 * @param mixed o
	 * @return boolean
	 */
	,isEmpty: function(o){
		return null === o || undefined === o || '' === o
				|| (function(){
						for(var i in o){
							if(o.hasOwnProperty(i)) return false;
						}
						return true;
					})();
	}
	
	/**
	 * check if object is String
	 * @param mixed o
	 * @return boolean
	 */
	,isString: function(o){
		return 'string' == typeof o;
	}
	
	/**
	 * check if object is Number
	 * @param mixed o
	 * @return boolean
	 */
	,isNumber: function(o){
		return 'number' == typeof o;
	}
	
	/**
	 * check if object is Number of integer
	 * @param mixed o
	 * @param boolean unsigned [optional]
	 * @return boolean
	 */
	,isInt: function(o, unsigned){
		var int = parseInt(o);
		return unsigned? int === o && o >= 0 : int === o;
	}
	
	/**
	 * check if object is Number of float
	 * @param mixed o
	 * @param boolean unsigned [optional]
	 * @return boolean
	 */
	,isFloat: function(o, unsigned){
		var float = parseFloat(o);
		return unsigned? float === o && o >= 0 : float === o;
	}
	
	/**
	 * check if object is Array
	 * @param mixed o
	 * @return boolean
	 */
	,isArray: function(o){
		return 'Array' == $$.getType(o);
	}
	
	/**
	 * check if object is Object
	 * @param mixed o
	 * @return boolean
	 */
	,isObject: function(o){
		return 'Object' == $$.getType(o);
	}
	
	/**
	 * check if object is Date
	 * @param mixed o
	 * @return boolean
	 */
	,isDate: function(o){
		return 'Date' == $$.getType(o);
	}
	
	/**
	 * check if object is RegExp
	 * @param mixed o
	 * @return boolean
	 */
	,isRegExp: function(o){
		return 'RegExp' == $$.getType(o);
	}
	
	/**
	 * check if object is Function
	 * @param mixed o
	 * @return boolean
	 */
	,isFunction: function(o){
		return 'Function' == $$.getType(o);
	}
	,isDom: function(o){
		return /HTML(\w*)Element/.test($$.getType(o));
	}
	,isEl: function(o){
		return o instanceof $$.Element;
	}
});

/**
 * Put some member to $$ object such as getUA, isFF,...
 */
$$.put($$, (function(){
	var ua = navigator.userAgent.toLowerCase();
	
	return {
		isFF: /firefox/.test(ua)
		,isChrome: /chrome/.test(ua)
		,isOpera: /opera/.test(ua)
		,isIE: /msie/.test(ua)
		,isLinux: /linux/.test(ua)
		,isWindow: /windows|win32/.test(ua)
		,isMac: /macintosh|mac os x/.test(ua)
		,isHttps: /https/.test(window.location.protocol)
	};
})());

/**
 * 
 */
$$.put($$, {
	VALUE_HALT: '$$--$$--$$'
	,prefix: 'cf'
	,emptyFn: function(){}

	/**
	 * Get a sequence number
	 * @return int
	 */
	,seq: function(){
		var i = 0;
		return function(){
			i ++;
			return i;
		};
	}()
	
	/**
	 * Create a/an namespace
	 * @params arguments name of namespace to created
	 * @return void
	 */
	,ns: function(/** arguments **/){
		for(var i = 0; i < arguments.length; ++i){
			var o = window
				,n = arguments[i]
				,parts = n.split('.');
				
			for(var j = 0; j < parts.length; ++j){
				o = o[parts[j]] = o[parts[j]] || {};
			}
		}
	}
	
	/**
	 * Iterates an array calling the supplied function
	 * @param array|object|string o
	 * @param function fn
	 * @param object oFnCreate [optional] the object for create callback function
	 * @return object
	 */
	,each: function(o, fn, oFnCreate){
		if(oFnCreate) fn = fn.createFn(oFnCreate);
		if($$.isArray(o)){
			for(var i = 0, len = o.length; i < len; ++i){
				fn(o[i], i, o);
			}
			return o;
		}
		
		for(var i in o){
			fn(o[i], i, o);
		}
		return o;
	}
});

$$.put($$, (function(){
	var ready = []
		,dcl = function(){
			document.removeEventListener('DOMContentLoaded', dcl, false);
			ready.each(function(fn){
				fn();
			});
		};
	document.addEventListener('DOMContentLoaded', dcl, false);
		
	return {
		ready: function(fn, oFnCreate){
			fn = fn.createFn(oFnCreate);
			ready.push(fn);
		}
	};
})());

$$.put($$, {
	encode: function(o){
		return JSON.stringify(o);
	}
	,decode: function(s){
		return JSON.parse(s);
	}
	
	,error: function(msg){
		throw msg;	
	}
	
	,urlEncode: function(o){
		var parts = [];
		for(var i in o){
			if($$.isArray(o[i])){
				o[i] = o[i].each(encodeURIComponent);
				parts.push(i + '[]=' + o[i].join('&' + i + '[]='));
			}else{
				parts.push(i + '=' + encodeURIComponent(o[i]));
			}
		}
		return parts.join('&');
	}
	,urlDecode: function(s){
		var parts = s.split('&')
			,o = {}
			,name
			,value;
			
		parts.each(function(p){
			p = p.split('=');
			name = decodeURIComponent(p[0]);
			value = decodeURIComponent(p[1]);
			o[name] = 'undefined' == o[name]? value : [].concat(o[name]).concat(value);
		});
		
		return o;
	}
	,urlAppend : function(url, s){
        if(!$$.isEmpty(s)){
        	if($$.isObject(s)) s = $$.urlEncode(s);
            return url + (url.indexOf('?') === -1 ? '?' : '&') + s;
        }
        return url;
    }
});

$$.put($$, {
	getDom: function(o){
		if($$.isString(o)) return document.querySelector(o);
		else if($$.isDom(o)) return o;
		else if($$.isEl(o)) return o.dom;
		
		return $$.DomHelper.create(o);
	}
	,getEl: function(o){
		if($$.isEl(o)) return o;
		return new $$.Element(o);
	}
});

$$.ns(
	'$$.util'
	,'$$.ajax'
	,'$$.dd'
	,'$$.form'
);